/**
  ******************************************************************************
  * @file
  * @brief
  * @author samuel-zsy
  * @version V1.0.0
  * @date 2018/07/17
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "lwip_app.h"
#include "w5500_fun.h"
#include "w5500.h"
#include "w5500_socket.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "A_shell.h"
#include "A_aclass_protocol.h"
#include "ParmManage.h"
#include "main_task.h"
/* Private defines -----------------------------------------------------------*/
//#define ETHNETER_DEBUG   //TCP回环测试

//#define SOCKET_BUFF_NUM         0x1000  //4096B


//任务函数
static SemaphoreHandle_t w5500_Sem;

/* Private Struct  -----------------------------------------------------------*/

/* Private enum    -----------------------------------------------------------*/


/* Private Variable  ---------------------------------------------------------*/
//Pointer

//Array

//Const
uint8_t SocketBuff[SOCKET_BUFF_NUM];
//uint8_t txsize[_WIZCHIP_SOCK_NUM_] = {4,4,0,0,0,0,0,0};
//uint8_t rxsize[_WIZCHIP_SOCK_NUM_] = {4,4,0,0,0,0,0,0};
uint8_t txsize[_WIZCHIP_SOCK_NUM_] = {8,8,0,0,0,0,0,0};
uint8_t rxsize[_WIZCHIP_SOCK_NUM_] = {8,8,0,0,0,0,0,0};

void loopback_tcp_server(SOCKET s, uint16_t port);
void loopback_udp(SOCKET s, uint16_t port);
struct Socket_Struct WorkSocket[SOCKET_EEFECTIVE_NUM] = 
{
	{
		.socket_num = SOCKET_NUMBER_1,
		.local.ip = {192,168,1,1},
		.local.port = 5005,
		.buff = SocketBuff,
		.buff_len = 0,
		.connect_flag = false,
        .last_connect_flag = true,    
		.loopback = loopback_tcp_server,		
	},
	{
		.socket_num = SOCKET_NUMBER_2,
		.local.ip = {0,0,0,0},
		.local.port = 1500,
		.remote.ip = {255,255,255,255},
		.remote.port = 2001,
		.buff = SocketBuff,
		.buff_len = 0,
		.connect_flag = false,	
        .last_connect_flag = true,    
		.loopback = loopback_udp,		
	}
};

static uint8_t tcp_connect_flag = 0;
/* Private function prototypes -----------------------------------------------*/
static uint8_t BroadCastip[4] = {255,255,255,255};
CONFIG_MSG  ConfigMsg;
CONFIG_MSG  ReadMsg;
/***********************************************************************************
 * @brief
 * ex:
 * @par
 * None
 * @par Called functions:
 * None
 * @retval void None
*
* 功能说明：初始化W5500 TCP连接的 IP,MAC,GATE,SumMask
* 输入参数：enum PP_CHANGE_FLAG - TCP通信IP参数设置标识量保存地址，0xAA: 网口参数已被设置，其他：未设置网口参数
*           enum PP_LOCAL_PORT_CDT - TCP通信端口号保存地址索引值
*           enum PP_LOCAL_IP_CDT - TCP通信IP保存地址索引值
*           enum PP_WAY_CDT - TCP通信网关保存地址索引值
*           enum PP_SUB_MASK_CDT - TCP通信子网掩码保存地址索引值
*           enum PP_MAC_CDT - MAC地址保存地址索引值
* 输出参数：CONFIG_MSG  ConfigMsg - TCP通信IP参数
* 返回值：无
 **********************************************************************************/
void Set_Default(void)
{
  uint8_t sock_reg_value;
  uint32_t change_flag = 0;
    
  uint8_t TEMP =0xAA;
  PPItemIWrite(PP_CHANGE_FLAG, (uint8_t *)(&TEMP), 4);
  PPItemRead(PP_CHANGE_FLAG, (uint8_t *)(&change_flag), 4);
	if(!(change_flag == 0xAAu))
//    if(1)
	{
		ConfigMsg.lip[3] = 15;//20;
		ConfigMsg.lip[2] = 200;
		ConfigMsg.lip[1] = 168;
		ConfigMsg.lip[0] = 192;
					
		ConfigMsg.gw[3] = 1;
		ConfigMsg.gw[2] = 1;
		ConfigMsg.gw[1] = 168;
		ConfigMsg.gw[0] = 192;

		ConfigMsg.sub[3] = 0;
		ConfigMsg.sub[2] = 255;
		ConfigMsg.sub[1] = 255;
		ConfigMsg.sub[0] = 255;

		ConfigMsg.mac[0] = 0x5C;
		ConfigMsg.mac[1] = 0x85;
		ConfigMsg.mac[2] = 0x7f;
		ConfigMsg.mac[3] = 0xED;
		ConfigMsg.mac[4] = 0x2C;
		ConfigMsg.mac[5] = 0xf3;
		//mac_temp = STMFLASH_ReadID();  //通过设备ID做MAC地址后三位
		//ConfigMsg.mac[3] = mac_temp&0xFF;
		//ConfigMsg.mac[4] = (mac_temp>>8)&0xFF;
		//ConfigMsg.mac[5] = (mac_temp>>16)&0xFF;

		PPItemIWrite(PP_LOCAL_PORT_CDT,(uint8_t*)&WorkSocket[0].local.port, 2);
		PPItemIWrite(PP_LOCAL_IP_CDT,(uint8_t*)ConfigMsg.lip, 4);	 //从Flash中读取本地IP
		PPItemIWrite(PP_WAY_CDT,(uint8_t*)ConfigMsg.gw, 4);          //从Flash中读取网关
		PPItemIWrite(PP_SUB_MASK_CDT,(uint8_t*)ConfigMsg.sub, 4);    //从Flash中读取子网掩码
		PPItemIWrite(PP_MAC_CDT,(uint8_t*)ConfigMsg.mac, 6);         //从Flash中读取物理地址		
	}
	else
	{
		PPItemRead(PP_LOCAL_PORT_CDT,(uint8_t*)&WorkSocket[0].local.port, 2);
		WorkSocket[1].local.port = WorkSocket[0].local.port;
		PPItemRead(PP_LOCAL_IP_CDT,(uint8_t*)ConfigMsg.lip, 4);	  //从Flash中读取本地IP
		PPItemRead(PP_WAY_CDT,(uint8_t*)ConfigMsg.gw, 4);         //从Flash中读取网关
		PPItemRead(PP_SUB_MASK_CDT,(uint8_t*)ConfigMsg.sub, 4);   //从Flash中读取子网掩码
		PPItemRead(PP_MAC_CDT,(uint8_t*)ConfigMsg.mac, 6);        //从Flash中读取物理地址
	}

	w5500sysinit(txsize, rxsize);      //分配2个Socket的发送和接收缓冲大小
	//iinchip_init();                  //复位内部寄存器
	//Delay_ms(5);

	//setMR(MR_PB);                      //MR模式寄存器  启用Ping Block 模式(开启会ping不通)   关闭：网络唤醒，PPPOE，强迫ARP
	setGAR(ConfigMsg.gw);              //设置网关
	setSUBR(ConfigMsg.sub);            //设置子网掩码
	setSHAR(ConfigMsg.mac);            //设置物理地址
	setSIPR(ConfigMsg.lip);            //设置IP
	
	uint8_t tmp = getMR();
	getGAR(ReadMsg.gw);              //读取网关
	getSUBR(ReadMsg.sub);            //读取子网掩码
	getSHAR(ReadMsg.mac);            //读取物理地址
	getSIPR(ReadMsg.lip);            //读取IP
	
	setINTLEVEL(374);                  //10us 低电平中断定时器寄存器 (374+1)*4/150 us
	//中断屏蔽寄存器   PPPOE关闭中断  Magic Packet关闭中断
	setIMR(IR_PPPoE|IR_MAGIC);
	setRTR(1000);                       //10ms超时重传
	setRCR(4);                         //重传次数1+1=2次
    
    sock_reg_value = getSn_MR(0);  // 获取当前套接字的模式寄存器值
    sock_reg_value |= Sn_MR_ND;         // 设置ND（No Delay）位，禁用Nagle算法
    setSn_MR(0, sock_reg_value);   // 更新模式寄存器
    sock_reg_value = getSn_MR(0);

	//setPHYCFGR(PHYCFGR_OPMDC_100H);    //硬件配置模式，半双工，100MHZ

//    uint8_t ip[4];
//    getSIPR(ip);
//    Ashell_print("get ip :%d:%d:%d:%d\r\n",ip[0],ip[1],ip[2],ip[3]);

#ifdef __DEF_IINCHIP_DBG__
	uint32_t status;
    status = getPHYCFGR();

	if(status & 0x01)  //打印连接状态
	{
		Ashell_print("\r\nIntneter connect.");
	}
	else
	{
		Ashell_print("\r\nIntneter break.");
	}
	if(status & 0x02)  //打印速度状态
	{
		Ashell_print("\r\nIntneter working speed 100MHZ.");
	}
	else
	{
		Ashell_print("\r\nIntneter working status 10MHZ.");
	}
	if(status & 0x04)  //打印工作状态 full duplex
	{
		Ashell_print("\r\nIntneter working status full duplex.");
	}
	else
	{
		Ashell_print("\r\nIntneter working status half duplex.");
	}
	Ashell_print("\r\nW5500 register version:%d",getVERSIONR());   //打印W5500的·寄存器版本
#endif

	//Sn 初始化
	setSn_MR(WorkSocket[SOCKET_NUMBER_2].socket_num, Sn_MR_UDP|Sn_MR_MULTI); //UDP 开启广播的功能
}

/***********************************************************************************
 * @brief  初始化W5500 SPI接口配置
 * ex:
 * @par
 * None
 * @par Called functions:
 * None
 * @retval None
 **********************************************************************************/
void LWIP_App_Init(void)
{
//	CPU_SR_ALLOC();
	
	W5500_Init();
	
	SoftDelay_ms(50);
	
	memset(SocketBuff,0,SOCKET_BUFF_NUM);

	Ashell_print("\r\nConfig and Connect Eth\r\n");

	Set_Default();

  //init_dhcp_client();	

	w5500_Sem = xSemaphoreCreateMutex();
}

/***********************************************************************************
 * @brief TCP(server) protocol function test function
 * ex:
 * @note  This function is used for functional testing of the TCP protocol
          to implement client(W5500 bord as client) & server(PC as server)
			  	data inter-transmission functions. The code is for reference only.
 * @par  s   : Specifies the socket number to use.
 *       port: Specify the port number used by the client.
 * @par Called functions:
 * None
 * @retval None
 **********************************************************************************/
void loopback_tcp_server(SOCKET s, uint16_t port)
{
	uint8_t client_ip_buff[4]={0};
    
    switch(getSn_SR(s))									 //获取	SOCKET s的状态
	{
		case SOCK_INIT:
			listen(s);
			WorkSocket[s].connect_flag = false;
		break;

		case SOCK_LISTEN:
			//Ashell_print("Keep listening...\r\n");
		break;

		case SOCK_ESTABLISHED:
			if(getSn_IR(s) & Sn_IR_CON)
			{
				setSn_IR(s, Sn_IR_CON);			// Sn_IR的CON位置1，通知w5500建立连接
			}
			if(!WorkSocket[s].connect_flag)              //打印客户端IP和端口   首次连接的情况下
			{
				getSn_DIPR(s,client_ip_buff);

				Ashell_print("Connected_IP = %d.%d.%d.%d\r\n",client_ip_buff[0],client_ip_buff[1],client_ip_buff[2],client_ip_buff[3]);

				Ashell_print("Connected_PORT = %d \r\n",getSn_DPORT(s));

				WorkSocket[s].connect_flag = 1;
				
				//Console_SetStatus(DEVICE_ONLINE);
			}
			if ((WorkSocket[s].buff_len = getSn_RX_RSR(s)) > 0)
			{
				if (WorkSocket[s].buff_len > getSn_RxMAX(s)) WorkSocket[s].buff_len = getSn_RxMAX(s);

				//myprintf("RSR_len = %d\r\n",recv_len);

				#ifdef ETHNETER_DEBUG
					memset(WorkSocket[s].buff,0,SOCKET_BUFF_NUM);
					
				    //回环测试
					recv(s,WorkSocket[s].buff,WorkSocket[s].buff_len);
					tcp_senddata(WorkSocket[s].buff,WorkSocket[s].buff_len);
					//send(s,WorkSocket[s].buff,WorkSocket[s].buff_len);
				#else
					memset(WorkSocket[s].buff,0,SOCKET_BUFF_NUM);
				
					recv(s,WorkSocket[s].buff,WorkSocket[s].buff_len);
					Aclass_CmdReceive_Notify("tcp", 0, (uint8_t *)WorkSocket[s].buff, WorkSocket[s].buff_len, IN_LOOP);

//                    if (IAP_GetMode() == IAP_MODE_NORMAL) {
//                        TCP_Load_RecvQueue(WorkSocket[s].buff, WorkSocket[s].buff_len);
//                    } else {
//                        IAP_PushMainData(WorkSocket[s].buff, WorkSocket[s].buff_len);
//                    }
				#endif
			}

		break;

		case SOCK_CLOSE_WAIT:												         // Socket等待关闭
			close(s);
		break;

		case SOCK_CLOSED:														         // Socket关闭
        WorkSocket[s].connect_flag = false;
        WorkSocket[s].last_connect_flag = false;

        Ashell_print("\r\n%d : Loop-Back TCP Started. port :%d\r\n", s, port);
		if(socket(s,Sn_MR_TCP,port,0x00) == s)
        {
            listen(s);  // 重新启动监听
            WorkSocket[s].connect_flag = false;
        }
		break;
	}
}

/***********************************************************************************
 * @brief TCP Server发送数据
 * ex:
 * @par Aclass_Hardware_Register("tcp", tcp_senddata_callback, tcp_scan_flush);
 * None
 * @par Called functions:
 * None
 * @retval void None
 * 
 * 输入参数：uint8_t *data - 待发送的数据 
 *           uint16_t len - 待发送的数据字节数
 * 输出参数：无
 * 返回值：发送数据的字节数
 **********************************************************************************/
uint8_t tcp_senddata(uint8_t *data, uint16_t len)
{
	int32_t status;
	uint8_t count = 0;
	uint16_t tx_buff_num= 0;

	if(data == NULL || len == 0)
	{
		return 0;
	}

	for(uint8_t ii=0; ii<SOCKET_EEFECTIVE_NUM-1; ii++)
	{
		if(!WorkSocket[ii].connect_flag)
		{
			continue;
		}

		if(pdFALSE == xSemaphoreTake(w5500_Sem, portMAX_DELAY)  )
		{
			Ashell_print("w5500_Sem taken failed!\r\n");
		}
		
		
		tx_buff_num = getSn_TxMAX(WorkSocket[ii].socket_num);
		count = len /tx_buff_num;		
		
		for(uint8_t ii=0;ii<count;ii++)
		{
			status = send(WorkSocket[ii].socket_num,(uint8_t*)data,tx_buff_num);
			if(status <= 0)
			{
				if(status == SOCK_BUSY)
				{
					SoftDelay_ms(1);
					continue;
				}
				Ashell_print("\a\a\a \r\n SOCKET %d : System Fatal Error.error code: %d \r\n", WorkSocket[ii].socket_num,status);
			}
			data += tx_buff_num;
		}
		
		tx_buff_num = len%tx_buff_num;
		if(tx_buff_num)
		{
			status = send(WorkSocket[ii].socket_num,(uint8_t*)data,tx_buff_num);
			if(status <= 0)
			{
				Ashell_print("\a\a\a \r\n SOCKET %d : System Fatal Error.error code: %d \r\n", WorkSocket[ii].socket_num,status);
			}
		}
		
		if(pdFALSE == xSemaphoreGive(w5500_Sem))
		{
			Ashell_print("w5500_Sem release failed!\r\n");
		
		}
	}
	return len;
}

/*************************************************************************************
* @brief  UDP(client) protocol function test function
* @note   This function is used for functional testing of the UDP protocol to implement client(W5500 bord as client) & server(PC as server)
*         data inter-transmission functions. The code is for reference only.
* @param  s   : Specifies the socket number to use.
* @param  port: Specify the port number used by the client.
* @retval NONE.
*************************************************************************************/
void loopback_udp(SOCKET s, uint16_t port)
{
	switch (getSn_SR(s))
	{
		case SOCK_UDP:
			if ((WorkSocket[s].buff_len = getSn_RX_RSR(s)) > 0) 			/* check Rx data */
			{
				//myprintf("RSR_len = %d\r\n",getSn_RX_RSR(s));
				if (WorkSocket[s].buff_len > getSn_RxMAX(s))
				{
					WorkSocket[s].buff_len = getSn_RxMAX(s);	/* if Rx data size is lager than MAX BUFF SIZE */
				}
				//myprintf("Get a UDP Packet\r\n");
				#ifdef ETHNETER_DEBUG
					memset(WorkSocket[s].buff,0,SOCKET_BUFF_NUM);

					WorkSocket[s].buff_len = recvfrom(s, WorkSocket[s].buff, WorkSocket[s].buff_len, (uint8_t*)&WorkSocket[s].remote.ip, &WorkSocket[s].remote.port);			/* read the received data */

					udp_senddata((uint8_t *)WorkSocket[s].buff, WorkSocket[s].buff_len);
				#else
					//Answer_TimeStarts();  //开始计时

					memset(WorkSocket[s].buff,0,SOCKET_BUFF_NUM);

					WorkSocket[s].buff_len = recvfrom(s, WorkSocket[s].buff, WorkSocket[s].buff_len, (uint8_t*)&WorkSocket[s].remote.ip, &WorkSocket[s].remote.port);			/* read the received data */
					//UDP_Analyze_Protocol((char*)WorkSocket[s].buff,WorkSocket[s].buff_len);   //直接解析
				
					WorkSocket[s].buff_len = sprintf((char*)WorkSocket[s].buff,"%d,%d",WorkSocket[s].local.port,tcp_connect_flag);  //sprintf函数返回值：buffer指向的字符串的长度
					udp_senddata(WorkSocket[s].buff,WorkSocket[s].buff_len);
				#endif
			}
		break;
		case SOCK_CLOSED:                            /* if a socket is closed */
			//Ashell_print("\r\n%d : Loop-Back UDP Started. port :%d\r\n", s, port);
			if(socket(s,Sn_MR_UDP,port,0) == 0)    /* reinitialize the socket */
			{
				//Ashell_print("\a%d : Fail to create socket.\r\n",s);
			}
	//		delay_ms(2000);  //发送广播需要延时一段时间 2s左右
	//		sendto(s, (uint8*)"123456789", 9,(uint8_t*)&WorkSocket[s].remote.ip, WorkSocket[s].remote.port);	//广播一组数据
		break;
	}
}

/***********************************************************************************
 * @brief UDP 发送数据
 * ex:
 * @par
 * None
 * @par Called functions:
 * None
 * @retval void None
 **********************************************************************************/
uint8_t udp_senddata(uint8_t *data, uint16_t len)
{
	int32_t status;
	uint8_t count = 0;
	uint16_t tx_buff_num= 0;
	if(data == NULL || len == 0)
	{
		return 0;
	}
	tx_buff_num = getSn_TxMAX(WorkSocket[SOCKET_NUMBER_2].socket_num);
	count = len /tx_buff_num;
	for(uint8_t ii=0;ii<count;ii++)
	{
		// status = sendto(WorkSocket[SOCKET_NUMBER_4].socket_num, (uint8*)data, tx_buff_num,(uint8_t*)&WorkSocket[SOCKET_NUMBER_4].remote.ip, WorkSocket[SOCKET_NUMBER_4].remote.port);
		status = sendto(WorkSocket[SOCKET_NUMBER_2].socket_num, (uint8_t*)data, tx_buff_num,(uint8_t*)&BroadCastip, WorkSocket[SOCKET_NUMBER_2].remote.port);

		if(status <= 0)
		{
			if(status == SOCK_BUSY)
			{
				SoftDelay_ms(1);
				continue;
			}
			Ashell_print("\a\a\a \r\n SOCKET %d : System Fatal Error.error code: %d", WorkSocket[SOCKET_NUMBER_2].socket_num,status);
		}

		data += tx_buff_num;
	}
	tx_buff_num = len%tx_buff_num;
	if(tx_buff_num)
	{
		// status = sendto(WorkSocket[SOCKET_NUMBER_4].socket_num,(uint8*)data, tx_buff_num,(uint8_t*)&WorkSocket[SOCKET_NUMBER_4].remote.ip, WorkSocket[SOCKET_NUMBER_4].remote.port);
		status = sendto(WorkSocket[SOCKET_NUMBER_2].socket_num, (uint8_t*)data, tx_buff_num,(uint8_t*)&BroadCastip, WorkSocket[SOCKET_NUMBER_2].remote.port);
	}
	return len;
}
/***********************************************************************************
 * @brief 功能说明：通信任务进程
 * ex:
 * @par   输入参数：SOCKET_EEFECTIVE_NUM - SOCKET个数
 *                  struct Socket_Struct WorkSocket[SOCKET_EEFECTIVE_NUM] - SOCKET通信参数
 * @par   输出参数：uint8_t disconnect_flag - 断联标识：0-未断联，1-断联
 * None
 * @retval返回值：无
 **********************************************************************************/
void eth_loopback_task(void *p_arg)
{
	while(1)
	{
		vTaskDelay(pdMS_TO_TICKS(10));
		for(uint8_t ii=0; ii<SOCKET_EEFECTIVE_NUM; ii++)
		{        
            WorkSocket[ii].loopback(WorkSocket[ii].socket_num,WorkSocket[ii].local.port);			
		}

		if((!WorkSocket[0].connect_flag) && (!WorkSocket[1].connect_flag))
		{
			//Console_SetStatus(DEVICE_OFFLINE);	//控制台掉线
		}
		else
		{
			//Console_SetStatus(DEVICE_ONLINE);	//控制台在线
		}
        
	}
}


/******************* (C) COPYRIGHT 2018 CIQTEK Samuel *****END OF FILE****/
